#include "ScreenApp.h"

screen ScreenApp::m_old_buff;
screen ScreenApp::m_new_buff;

void ScreenApp::diff()//finding the difference between the old matrix and the new one and draws the differnce on the screen.
{
	for(int i=0; i < 80; i++)
		for(int j=0; j < 25; j++)
		{
			if( m_old_buff.getCell(i,j) != m_new_buff.getCell(i,j) )
			{
				gotoxy(i,j);
				cout << m_new_buff.getCell(i,j);
			}
		}
		Sleep(10);
}

void ScreenApp::run(ShapeContainer &shape_cont, bool withTransfer)//draws the shapes with transfer from one side of the screen to the other and thru each other.
{
	bool withHiding = true;
	ShapeContainer work_cont;
	work_cont.copyContainer(shape_cont);
	m_old_buff.getScreenFromList( work_cont.getList() );
	clrscr();
	work_cont.drawList(!withHiding);
	while(!_kbhit() || _getch() != ESC)
	{
		work_cont.calcMove(withTransfer,work_cont);
		m_new_buff.getScreenFromList( work_cont.getList() );
		diff();
		memcpy(&m_old_buff,&m_new_buff,sizeof(screen));
		m_new_buff.initScr();
	}
}

void ScreenApp::circleCollisionAux(list<Shape*>::iterator itr, ShapeContainer &work_cont)//checks the collisions between circles
{
	list<Shape*>::iterator tmp;
	bool x_axis = true;

	int l_radius = ((MyCircle*)(*itr))->getRadius();
	int f = 1 - l_radius;
	int ddF_x = 1;
	int ddF_y = -2 * l_radius;
	int x = 0;
	int y = l_radius;
	int cx = (*itr)->getX();
	int cy =(*itr)->getY();
	int calc_expression = int( sqrt(( double(l_radius) * double(l_radius) / 2.0)));
	
	updateNewScreen(work_cont);
	//check special points
	if( !specialCheck(itr,work_cont,cx,cy))
		return;

	//check intire circle.
	while( (x+1) < y)
	{
		if(f >= 0) 
		{
			y--;
			ddF_y += 2;
			f += ddF_y;
		}
		x++;
		ddF_x += 2;
		f += ddF_x;    

		//if moving right
		if((*itr)->getShiftX() > 0  )
		{		
			if (!checkIfClear(itr, work_cont, cx + l_radius + 1, cy))
				break;
			if (!checkIfClear(itr, work_cont, cx + x + 1, cy + y))
				break;
			if (!checkIfClear(itr, work_cont, cx + x + 1, cy - y))
				break;
			if (!checkIfClear(itr, work_cont, cx + y + 1, cy + x))
				break;
			if (!checkIfClear(itr, work_cont, cx + y + 1, cy - x))
				break;
		}

		//moving left
		else if((*itr)->getShiftX() < 0  )
		{
			if (!checkIfClear(itr,work_cont,cx - y - 1, cy - x))
				break;
			if (!checkIfClear(itr,work_cont,cx - x - 1, cy - y))
				break;
			if (!checkIfClear(itr,work_cont,cx - l_radius - 1, cy))
				break;
			if (!checkIfClear(itr,work_cont,cx - y - 1, cy + x))
				break;	
			if (!checkIfClear(itr,work_cont,cx - x - 1, cy + y))
				break;
		}

		//moving down
		if((*itr)->getShiftY() > 0  )
		{
			if (!checkIfClear(itr,work_cont,cx + y, cy + x + 1))
				break;
			if (!checkIfClear(itr,work_cont,cx + x, cy + y + 1))
				break;
			if (!checkIfClear(itr,work_cont,cx, cy + l_radius + 1))
				break;
			if (!checkIfClear(itr,work_cont,cx - y, cy + x + 1))
				break;		
			if (!checkIfClear(itr,work_cont,cx - x, cy + y + 1))
				break;
		}

		//moving up
		else if((*itr)->getShiftY() < 0  )
		{
			if (!checkIfClear(itr,work_cont,cx - y, cy - x - 1))
				break;
			if (!checkIfClear(itr,work_cont,cx - x, cy - y - 1))
				break;
			if (!checkIfClear(itr,work_cont,cx, cy - l_radius - 1))
				break;
			if (!checkIfClear(itr,work_cont,cx + x, cy - y - 1))
				break;
			if (!checkIfClear(itr,work_cont,cx + y, cy - x - 1))
				break;
		}
	}
}

void ScreenApp::rectCollisionAux(list<Shape*>::iterator itr, ShapeContainer &work_cont)//checks the collisions between rectangles
{
	bool not_found = true;
	updateNewScreen(work_cont);
	//if moving right
	if((*itr)->getShiftX() > 0  )
	{
		for(int x = (*itr)->getX() + ((MyRectangle*)(*itr))->getWidth(),y = (*itr)->getY();
			y < ((*itr)->getY() + ((MyRectangle*)(*itr))->getHeight()) && not_found;
			y++) 
			not_found = checkIfClear(itr,work_cont,x,y);
	}
	//moving left
	else if((*itr)->getShiftX() < 0 )
	{
		for(int x = ((*itr)->getX() - 1),y = (*itr)->getY(); 
			y < ((*itr)->getY() + ((MyRectangle*)(*itr))->getHeight() - 1) && not_found; 
			y++)
			not_found = checkIfClear(itr,work_cont,x,y);
	}
	//moving down
	if((*itr)->getShiftY() > 0  )
	{
		for(int x = (*itr)->getX(),y = (*itr)->getY() + ((MyRectangle*)(*itr))->getHeight();
			x < ((*itr)->getX() + ((MyRectangle*)(*itr))->getWidth() - 1) && not_found; 
			x++) 
			not_found = checkIfClear(itr,work_cont,x,y);
	}
	//moving up
	else if((*itr)->getShiftY() < 0  )
	{
		for(int x = (*itr)->getX(), y = (*itr)->getY() - 1;
			x < ((*itr)->getX() + ((MyRectangle*)(*itr))->getWidth() - 1) && not_found; 
			x++) 
			not_found = checkIfClear(itr,work_cont,x,y);
	}
}

bool ScreenApp::checkIfClear(list<Shape*>::iterator itr, ShapeContainer &work_cont, int x, int y)
{
	bool all_clear = true;
	if( x >= 0 && y >= 0) //if inside screen
	{
		if(m_new_buff.getCell(x,y) == '*')
		{
			if( (*itr)->getShiftX() != 0 ) //x axis
			{
				list<Shape*>::iterator hit = work_cont.findContainingShape(x,y);

				if( hit != itr)
				{
					if( (*hit)->getShiftX() * (*itr)->getShiftX() < 0)
					{
						(*hit)->setShiftX( (*hit)->getShiftX() * -1);
						(*hit)->setShiftXTmp( (*hit)->getShiftXTmp() * -1);
					}
					(*itr)->setShiftX((*itr)->getShiftX() * -1);
					(*itr)->setShiftXTmp((*itr)->getShiftXTmp() * -1);
					all_clear = false;
				}
			}
			if( (*itr)->getShiftY() != 0 ) //y axis
			{
				list<Shape*>::iterator hit = work_cont.findContainingShape(x,y);
				if( hit != itr)
				{
					if( (*hit)->getShiftY() * (*itr)->getShiftY() < 0)
					{
						(*hit)->setShiftY( (*hit)->getShiftY() * -1);
						(*hit)->setShiftYTmp( (*hit)->getShiftYTmp() * -1);
					}
					(*itr)->setShiftY((*itr)->getShiftY() * -1);
					(*itr)->setShiftYTmp((*itr)->getShiftYTmp() * -1);

					all_clear = false;
				}
				else
					return all_clear;
			}
		}
		return all_clear;
	}
	return(!all_clear);
}

bool ScreenApp::specialCheck(list<Shape*>::iterator itr, ShapeContainer &work_cont, int cx, int cy)
{
	int calc_expression = (int)( sqrt( (double( ((MyCircle*)(*itr))->getRadius()) * double( ((MyCircle*)(*itr))->getRadius() ) / 2.0 )));
	bool all_clear = true;

	if ( (*itr)->getShiftX() > 0)
	{
		if (!checkIfClear(itr,work_cont,cx + calc_expression + 1, cy - calc_expression - 1))
			return !all_clear;
		if (!checkIfClear(itr,work_cont,cx + calc_expression + 1, cy - calc_expression + 1))
			return !all_clear;
	}
	else if( (*itr)->getShiftX() < 0)
	{
		if (!checkIfClear(itr,work_cont,cx - calc_expression - 1, cy + calc_expression + 1))
			return !all_clear;
		if (!checkIfClear(itr,work_cont,cx - calc_expression - 1, cy - calc_expression - 1))
			return !all_clear;
	}
	if( (*itr)->getShiftY() > 0)
	{
		if (!checkIfClear(itr,work_cont,cx + calc_expression + 1, cy - calc_expression - 1))
			return !all_clear;
		if (!checkIfClear(itr,work_cont,cx - calc_expression - 1, cy - calc_expression - 1))
			return !all_clear;
	}
	else if( (*itr)->getShiftY() < 0)
	{
		if (!checkIfClear(itr,work_cont,cx + calc_expression + 1, cy + calc_expression + 1))
			return !all_clear;
		if (!checkIfClear(itr,work_cont,cx - calc_expression - 1, cy + calc_expression + 1))
			return !all_clear;
	}
	return all_clear;
}

void ScreenApp::updateNewScreen( ShapeContainer &work_cont)
{
	m_new_buff.getScreenFromList(work_cont.getList());
}
